/*
 *  Copyright (C) 2009 Antonio Mata Chamorro
 *  
 *  Este programa es software libre: usted puede redistribuirlo y/o modificarlo
 *  bajo los t�rminos de la Licencia P�blica General GNU publicada
 *  por la Fundaci�n para el Software Libre, ya sea la versi�n 3
 *  de la Licencia, o (a su elecci�n) cualquier versi�n posterior.
 *  
 *  Este programa se distribuye con la esperanza de que sea �til, pero 
 *  SIN GARANT�A ALGUNA; ni siquiera la garant�a impl�cita
 *  MERCANTIL o de APTITUD PARA UN PROP�SITO DETERMINADO. 
 *  Consulte los detalles de la Licencia P�blica General GNU para obtener 
 *  una informaci�n m�s detallada. 
 *  
 *  Deber�a haber recibido una copia de la Licencia P�blica General GNU
 *  junto a este programa. 
 *  En caso contrario, consulte <http://www.gnu.org/licenses/>.
 *   
 */
package org.javahispano.javacup.tacticas_aceptadas.amata.com.amata.javacup.tacticas;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;

/**
 * SoccerTeam representa un equipo de f�tbol compuesto por {@link SoccerPlayer}.
 * Cada equipo se puede encontrar en 2 estados: atacando o defendiendo. seg�n
 * estos estados se les ordenara a los jugadores que ocupen su posicion
 * defensiva o ofensiva.
 * 
 * <p/>
 * 
 * Si se controla el bal�n se buscar� las mejores posiciones de pase para enviar
 * a un jugador all� (supporters). Si no se controla el bal�n se hara lo mismo
 * para determinar a donde pasara el contrario el bal�n y enviar a jugadores
 * propios a esas posiciones (defenders).
 * 
 * @author Antonio Mata Chamorro
 * */
public class SoccerTeam {
	public enum SoccerTeamState {
		defending, attacking
	};

	public static final int NUM_PLAYERS_IN_TEAM = 11;
	private int numDefenders = 2;
	private int numSupporters = 2;
	private float distanceForAttack = (float) Constantes.LARGO_AREA_GRANDE;
	private float distanceForDefense = 0;// (float)Constantes.LARGO_AREA_CHICA;

	private SoccerTeamState state;
	private SoccerMatch match;
	private SoccerTeam oppositeTeam;
	private SoccerPlayer attacker;
	private List<SoccerPlayer> players = new ArrayList<SoccerPlayer>();
	private Posicion[] playersPositions = new Posicion[NUM_PLAYERS_IN_TEAM];
	private Goal goal = new Goal();
	private SoccerPlayer goalKeeper;
	
	private SpotResults results = new SpotResults();
	private List<SoccerPlayer> auxPlayers = new ArrayList<SoccerPlayer>();
	private SoccerPlayer auxPlayer = null;

	public SoccerTeam(SoccerMatch match, boolean own, SituacionPartido sp) {
		this.match = match;

		// configuracion
		this.numDefenders = this.match.getConfiguration().getNumDefenders();
		this.numSupporters = this.match.getConfiguration().getNumSupporters();
		this.distanceForAttack = this.match.getConfiguration()
				.getDistanceForAttack();
		this.distanceForDefense = this.match.getConfiguration()
				.getDistanceForDefense();

		for (int i = 0; i < NUM_PLAYERS_IN_TEAM; i++) {
			SoccerPlayer player = new SoccerPlayer(this);
			if (own)
				player.setInfo(sp.detalleMisJugadores()[i],
						sp.misJugadores()[i], i);
			else
				player.setInfo(sp.detalleJugadoresRivales()[i],
						sp.rivales()[i], i);

			players.add(player);
		}
		this.playersPositions = sp.misJugadores();

		for (SoccerPlayer p : this.players) {
			if (p.getDetalle().esPortero()) {
				this.goalKeeper = p;
				break;
			}
		}

		this.state = sp.saco() ? SoccerTeamState.attacking
				: SoccerTeamState.defending;
	}

	public SoccerTeam(SoccerMatch match, boolean own, SituacionPartido sp,
			Posicion[] defensivePosicions, Posicion[] offensivePositions) {
		this(match, own, sp);

		for (int i = 0; i < players.size(); i++) {
			players.get(i)
					.setDefensiveHomeSpot(new Spot(defensivePosicions[i]));
			players.get(i)
					.setOffensiveHomeSpot(new Spot(offensivePositions[i]));
		}

	}

	public SoccerMatch getMatch() {
		return match;
	}

	public SoccerPlayer findClosestPlayer(Posicion posicion) {
		int[] index = posicion.indicesMasCercanos(playersPositions);

		return players.get(index[0]);
	}

	/**
	 * Primero se determina el estado actual del equipo
	 * <ul>
	 * <li>atacando: esto ocurre cuando se controla el bal�n
	 * (getMatch().isOwnControllingBall()) y se esta cerca del medio campo
	 * (distancia menor de
	 * {@link SoccerMatchConfiguration#getDistanceForAttack()}).</li>
	 * <li>defendiendo: cuando no se controla el bal�n
	 * (getMatch().isOwnControllingBall()) y el contrario se encuentra cerca de
	 * nuestra mitad del campo (distancia menor de
	 * {@link SoccerMatchConfiguration#getDistanceForDefense()}).</li>
	 * </ul>
	 * 
	 * Seg�n estos estados se les ordenar� a los jugadores que ocupen su
	 * posicion ofensiva o defensiva respectivamente.
	 * 
	 * <p/>
	 * 
	 * Si se controla el bal�n se buscara las mejores posiciones de pase para
	 * enviar a un jugador a ellas (supporters). Si no se controla el bal�n se
	 * hara lo mismo para determinar a donde pasara el contrario el bal�n y
	 * enviar a jugadores propios a esas posiciones (defenders). Para esto se
	 * utilizara getMatch().getBSSCalculator()
	 * 
	 * @see SoccerPlayer#getDefensiveHomeSpot()
	 * @see SoccerPlayer#getOffensiveHomeSpot()
	 * 
	 * */
	public void update(SituacionPartido sp, LinkedList<Comando> comandos) {
		// cambiamos el estado actual
		if (getMatch().getBallPosicion().getY() < 0) {
			// estoy en mi campo
			// controlo el bal�n y estoy muy cerca del medio campo
			if (getMatch().isOwnControllingBall()
					&& getMatch().getBallPosicion().getY() > -this.distanceForAttack) {
				this.state = SoccerTeamState.attacking;
			} else {
				this.state = SoccerTeamState.defending;
			}
		} else {
			// estoy en el campo contrario
			// controlo el bal�n o estoy muy cerca del medio campo
			if (getMatch().isOwnControllingBall()
					|| getMatch().getBallPosicion().getY() > this.distanceForDefense) {
				this.state = SoccerTeamState.attacking;
			} else {
				this.state = SoccerTeamState.defending;
			}
		}

		if (match.isOwnControllingBall()) {
			auxPlayers.clear();

			this.attacker = findClosestPlayer(getMatch().getBallPosicion());
			this.attacker.setMessage(new CatchMessage());

			auxPlayers.add(this.attacker);

			// log.info("now own attacker=" + this.attacker);

			// se busca las mejores posiciones de pase para el atacante actual
			results.clear();
			results.setNumber(getNumSupporters() + 1);
			match.getBSSCalculator().findSpots(getAttacker(), results);
			for (int i = 0; i < results.getNumber(); i++) {
				auxPlayer = findClosestPlayer(results.getSpot(i).getPosicion());

				if (auxPlayer != this.attacker
						&& auxPlayers.size() < getNumSupporters()) {
					auxPlayer.setMessage(new GotoMessage(results.getSpot(i),
							true));
					auxPlayers.add(auxPlayer);
					// log.info("order supporter " + auxPlayer + " go to spot "
					// + results.getSpot(i));
				}
			}
		} else {
			auxPlayers.clear();

			match.getOpponentTeam().setAttacker(
					findClosestPlayer(match.getBallPosicion()));

			// se ordena al jugador mas cercano a la pelota tratar de capturarla
			auxPlayer = findClosestPlayer(getMatch().getBallPosicion());
			this.attacker = auxPlayer;
			auxPlayer.setMessage(new CatchMessage());

			// se ordena a los defensores mas cercanos a la pelota ir a una
			// cierta posicion, menos al capturador
			results.clear();
			results.setNumber(getNumDefenders() + 1);
			match.getBSSCalculator().findSpots(
					match.getOpponentTeam().getAttacker(), results);
			for (int i = 0; i < results.getNumber(); i++) {
				auxPlayer = findClosestPlayer(results.getSpot(i).getPosicion());

				if (auxPlayer != this.attacker
						&& auxPlayers.size() < getNumDefenders()) {
					auxPlayer.setMessage(new GotoMessage(results.getSpot(i),
							false));
					auxPlayers.add(auxPlayer);
				}

				// log.info("order defender " + auxPlayer +
				// " press to attacker");
			}

			auxPlayers.add(this.attacker);
			// log.info("closest player to ball=" + this.attacker);
		}

		// mandamos ordenes a los jugadores seg�n el estado actual
		if (state == SoccerTeamState.defending) {

			// se ordena al resto de jugadores ir a su posicion defensiva
			for (SoccerPlayer p : this.players) {
				if (!auxPlayers.contains(p)) {
					p.setMessage(new GotoMessage(p.getDefensiveHomeSpot(),
							false));
				}
			}

		} else if (state == SoccerTeamState.attacking) {

			// se ordena al resto de jugadores ir a su posicion ofensiva
			for (SoccerPlayer p : this.players) {
				if (!auxPlayers.contains(p)) {
					p.setMessage(new GotoMessage(p.getOffensiveHomeSpot(),
							false));
				}
			}
		}
	}

	public SoccerPlayer getAttacker() {
		return attacker;
	}

	/**
	 * Numero de defensores del equipo. Este valor esta fijado por la
	 * configuracion del equipo.
	 * 
	 * @see SoccerMatchConfiguration#getNumDefenders()
	 * */
	public int getNumDefenders() {
		return numDefenders;
	}

	public void setNumDefenders(int numDefenders) {
		this.numDefenders = numDefenders;
	}

	/**
	 * Numero de soportes del equipo. Este valor esta fijado por la
	 * configuracion del equipo.
	 * 
	 * @see SoccerMatchConfiguration#getNumSupporters()
	 * */
	public int getNumSupporters() {
		return numSupporters;
	}

	public void setNumSupporters(int numSupporters) {
		this.numSupporters = numSupporters;
	}

	public List<SoccerPlayer> getPlayers() {
		return players;
	}

	public void setAttacker(SoccerPlayer attacker) {
		this.attacker = attacker;
	}

	/**
	 * Determina si alg�n jugador de este equipo puede golpear la pelota.
	 * 
	 * @param kickPosition
	 *            posicion desde la que hay que comprobar si un jugador disparar
	 * @return true si alg�no de nuestros jugadores puede golpear la pelota si
	 *         estuviera en kickPosition
	 * */
	public boolean canKickAnyPlayer(Posicion kickPosition) {
		for (SoccerPlayer p : this.players) {
			if (p.getPosicion().distancia(kickPosition) <= Constantes.DISTANCIA_CONTROL_BALON)
				return true;
		}

		return false;
	}

	@Override
	public String toString() {
		String aux = "state=" + state + ",isOwnControllingBall()="
				+ match.isOwnControllingBall() + ", attacker:" + this.attacker
				+ "\n";
		for (SoccerPlayer player : players) {
			aux += "\t" + player + "\n";
		}
		return aux;
	}

	public void setOppositeTeam(SoccerTeam oppositeTeam) {
		this.oppositeTeam = oppositeTeam;
	}

	/**
	 * Devuelve el equipo contra el que este equipo esta jugando
	 * */
	public SoccerTeam getOppositeTeam() {
		return oppositeTeam;
	}

	public void setPlayersPositions(Posicion[] playersPositions) {
		this.playersPositions = playersPositions;
	}

	public Goal getGoal() {
		return goal;
	}

	public SoccerPlayer getGoalKeeper() {
		return goalKeeper;
	}

}
